home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / rpc / rpcCall.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  25KB  |  839 lines

  1. /*
  2.  * rpcCall.c --
  3.  *
  4.  *      These are the top-level routines for the client side of Remote
  5.  *      Procedure Call - the routines do overhead tasks like setting up a
  6.  *      message, and managing client channels.  The network protocol for
  7.  *      the client side is in rpcClient.c.
  8.  *
  9.  * Copyright 1985 Regents of the University of California
  10.  * All rights reserved.
  11.  */
  12.  
  13. #ifndef lint
  14. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/rpc/rpcCall.c,v 9.18 92/12/13 18:21:27 mgbaker Exp $ SPRITE (Berkeley)";
  15. #endif /* not lint */
  16.  
  17.  
  18. #include <sprite.h>
  19. #include <stdio.h>
  20.  
  21. #include <rpc.h>
  22. #include <rpcPacket.h>
  23. #include <rpcClient.h>
  24. #include <rpcTrace.h>
  25. #include <rpcHistogram.h>
  26. #include <sys.h>
  27. #include <timerTick.h>
  28. #include <timer.h>
  29. #include <user/limits.h>
  30. /* Not needed if recov tracing is removed. */
  31. #include <recov.h>
  32.  
  33. /*
  34.  * So we can print out rpc names we include the rpcServer definitions.
  35.  */
  36. #include <rpcServer.h>
  37.  
  38. /*
  39.  * The client channel table is kept as an array of pointers to channels.
  40.  * rpcClient.h describes the contents of a ClientChannel.  The number
  41.  * of channels limits the parallelism available on the client.  During
  42.  * system shutdown, for example, there may be many processes doing
  43.  * remote operations (removing swap files) at the same time.
  44.  */
  45.  
  46. RpcClientChannel **rpcChannelPtrPtr = (RpcClientChannel **)NIL;
  47. int           rpcNumChannels = 8;
  48. int           numFreeChannels = 8;
  49.  
  50. /*
  51.  * The allocation and freeing of channels is monitored.
  52.  * A process might have to wait for a free RPC channel.
  53.  */
  54. Sync_Condition freeChannels;
  55. Sync_Semaphore rpcMutex = Sync_SemInitStatic("Rpc:rpcMutex");
  56.  
  57. /*
  58.  * There is sequence of rpc transaction ids that increases over time.
  59.  */
  60.  
  61. unsigned int rpcID = 1;
  62.  
  63. /*
  64.  * A boottime Id is used to help servers realize that a client has
  65.  * rebooted since it talked to it last.  It is zero for the first
  66.  * RPC, one that gets the time.  Then it is set to that time and does
  67.  * not change until we reboot.
  68.  */
  69.  
  70. unsigned int rpcBootID = 0;
  71. /*
  72.  * A count is kept of the number of RPCs made.
  73.  * The zero'th element is used to count attempts at
  74.  * RPCs with unknown RPC numbers - RPC number 0 is unused.
  75.  */
  76. int rpcClientCalls[RPC_LAST_COMMAND+1];
  77.  
  78. /*
  79.  * For one of the client policies for handling negative acknowledgements,
  80.  * we ramp down the number of channels used with the ailing server.  These
  81.  * data structures keep track of which servers are unhappy.
  82.  */
  83. typedef struct    UnhappyServer {
  84.     int        serverID;
  85.     Timer_Ticks    time;
  86. } UnhappyServer;
  87.  
  88. UnhappyServer    serverAllocState[8];
  89.  
  90. /* Initial back-off interval for negative acknowledgements. */
  91. unsigned int    channelStateInterval;
  92.  
  93. /* forward declaration */
  94. static Boolean GetChannelAllocState _ARGS_((int serverID, Timer_Ticks *time));
  95. static void SetChannelAllocState _ARGS_((int serverID, Boolean trouble));
  96. static void SetChannelAllocStateInt _ARGS_((int serverID, Boolean trouble));
  97.  
  98.  
  99. /*
  100.  *----------------------------------------------------------------------
  101.  *
  102.  * Rpc_Call --
  103.  *
  104.  *      Top-level interface for a client that makes a remote procedure
  105.  *      call.  Our caller has to pre-allocate all storage for the data
  106.  *      going to the server in the request message, and for the data
  107.  *      returning in the reply message.  The storage argument contains
  108.  *      pointers and sizes for this preallocated space.  Upon return
  109.  *    the data from the reply message will be in the specified storage
  110.  *    areas.
  111.  *
  112.  * Results:
  113.  *      An error code that either reflects an error in delivery/transport
  114.  *      or is an error code from the remote procedure.  Also, the storage
  115.  *      input specification is modified - the return parameter and data
  116.  *      size fields are updated to reflect the true size of the return
  117.  *      parameter and data blocks.  Finally, the return parameter and data
  118.  *      areas contain the results of the remote procedure call.
  119.  *
  120.  * Side effects:
  121.  *    There are no side effects on this machine except that some addressing
  122.  *    information is kept as a hint for future RPCs.  The semantics of
  123.  *    the remote procedure are unlimited on the server machine.
  124.  *
  125.  *----------------------------------------------------------------------
  126.  */
  127. ReturnStatus
  128. Rpc_Call(serverID, command, storagePtr)
  129.     int serverID;        /* Indicates the server for the RPC.  The
  130.                  * special value SERVER_BROADCAST is used
  131.                  * to specify a broadcast RPC.  The first
  132.                  * reply received is returned and subsequent
  133.                  * replies get discarded.  Broadcast RPCs
  134.                  * are NOT retried if there is no response. */
  135.     int command;        /* Rpc command.  Values defined in rpcCall.h */
  136.     Rpc_Storage *storagePtr;    /* Specifies buffer areas for request and
  137.                  * reply messages. */
  138. {
  139.     register RpcClientChannel *chanPtr;    /* Handle for communication channel */
  140.     register ReturnStatus error;    /* General error return status */
  141.     Time histTime;            /* Time for histogram taking */
  142.     unsigned    int srvBootID;        /* Boot time stamp from server, used to
  143.                      * track server reboots */
  144.     Boolean notActive = 0;        /* Not active flag from server */
  145.     unsigned int recovType = 0;        /* Whether rpc reply had recov flags. */
  146.  
  147.     if (serverID < 0 || serverID >= NET_NUM_SPRITE_HOSTS) {
  148.     printf("Rpc_Call, bad serverID <%d>\n", serverID);
  149.     return(GEN_INVALID_ARG);
  150.     } else if (serverID != RPC_BROADCAST_SERVER_ID &&
  151.            serverID == rpc_SpriteID) {
  152.     printf("Rpc_Call: Trying RPC #%d to myself\n", command);
  153.     return(GEN_INVALID_ARG);
  154.     } else if ((serverID == RPC_BROADCAST_SERVER_ID) &&
  155.            ! (command == RPC_FS_PREFIX ||
  156.           command == RPC_GETTIME)) {
  157.     panic("Trying to broadcast a non-prefix RPC");
  158.     return(GEN_INVALID_ARG);
  159.     }
  160. #ifdef TIMESTAMP
  161.     RPC_NIL_TRACE(RPC_CLIENT_A, "Rpc_Call");
  162. #endif /* TIMESTAMP */
  163.  
  164. allocAgain:
  165.     RPC_CALL_TIMING_START(command, &histTime);
  166.     chanPtr = RpcChanAlloc(serverID);
  167.  
  168. #ifdef TIMESTAMP
  169.     RPC_NIL_TRACE(RPC_CLIENT_B, "alloc");
  170. #endif /* TIMESTAMP */
  171.     /*
  172.      * Initialize the RPC request message header and put buffer
  173.      * specifications of our caller into the state of the channel.  This
  174.      * is once-per-rpc initialization that does not need to be repeated if
  175.      * we have to re-send.
  176.      */
  177.     RpcSetup(serverID, command, storagePtr, chanPtr);
  178.  
  179.     /*
  180.      * Update a histogram of RPCs made.  The zeroth element is used to
  181.      * count unknown rpcs
  182.      */
  183.     if (command > 0 &&
  184.     command <= RPC_LAST_COMMAND) {
  185.     rpcClientCalls[command]++;
  186.     } else {
  187.     /*
  188.      */
  189.     printf("Rpc_Call: unknown rpc command (%d)\n", command);
  190.     rpcClientCalls[0]++;    /* 0 == RPC_BAD_COMMAND */
  191.     }
  192. #ifdef TIMESTAMP
  193.     RPC_NIL_TRACE(RPC_CLIENT_C, "setup");
  194. #endif /* TIMESTAMP */
  195.  
  196.     /*
  197.      * Call RpcDoCall, which synchronizes with RpcClientDispatch,
  198.      * to do the send-receive-timeout loop for the RPC.
  199.      */
  200. /* Remove this debugging print stuff soon. */
  201.     if (command == RPC_ECHO_2 && recov_PrintLevel >= RECOV_PRINT_ALL) {
  202.     Sys_HostPrint(serverID, "Pinging server\n");
  203.     }
  204.     error = RpcDoCall(serverID, chanPtr, storagePtr, command,
  205.               &srvBootID, ¬Active, &recovType);
  206.     RpcChanFree(chanPtr);
  207.     
  208. #ifdef TIMESTAMP
  209.     RPC_NIL_TRACE(RPC_CLIENT_OUT, "return");
  210. #endif /* TIMESTAMP */
  211.  
  212.     RPC_CALL_TIMING_END(command, &histTime);
  213. /* This slow printing stuff should be removed soon. It's for debugging. */
  214.     if (command == RPC_ECHO_2 && recov_PrintLevel >= RECOV_PRINT_ALL) {
  215.     if (error == RPC_NACK_ERROR) {
  216.         Sys_HostPrint(serverID,
  217.             "Ping result bad: Nack error from server\n");
  218.     } else if (error == RPC_TIMEOUT || error == NET_UNREACHABLE_NET) {
  219.         Sys_HostPrint(serverID, "Ping result bad: server is dead.\n");
  220.     }
  221.     if (error == SUCCESS) {
  222.         Sys_HostPrint(serverID, "Pinged serverID successfully.\n");
  223.     }
  224.     }
  225.     if (error == RPC_NACK_ERROR) {
  226.     /*
  227.      * This error is only returned if the client policy for handling
  228.      * negative acknowledgements is to ramp down the number of channels
  229.      * used, so that's what we do.
  230.      */
  231.     SetChannelAllocState(serverID, TRUE);
  232.     goto allocAgain;
  233.     }
  234. #ifndef NO_RECOVERY
  235.     if (error == RPC_TIMEOUT || error == NET_UNREACHABLE_NET) {
  236.     if (command != RPC_ECHO_2) {
  237.         printf("<%s> ", rpcService[command].name);
  238.         Sys_HostPrint(serverID, "RPC timed-out\n");
  239.     }
  240.     Recov_HostDead(serverID);
  241.     } else {
  242.     Recov_HostAlive(serverID, srvBootID, TRUE, notActive, recovType);
  243.     }
  244. #endif /* NO_RECOVERY */
  245.     return(error);
  246. }
  247.  
  248. /*
  249.  *----------------------------------------------------------------------
  250.  *
  251.  * RpcSetup --
  252.  *
  253.  *      Initialize the RPC header for the request message and set up the
  254.  *      buffer specifications for the request and reply messages.  The
  255.  *      operations done here are done once before the request message is
  256.  *      sent out the first time, and they do not have to be re-done if the
  257.  *      request message needs to be re-sent.
  258.  *
  259.  * Results:
  260.  *    None.
  261.  *
  262.  * Side effects:
  263.  *      The Rpc header is initialized.  The buffer specifications for the
  264.  *      request and reply messages are set up.
  265.  *
  266.  *----------------------------------------------------------------------
  267.  */
  268. void
  269. RpcSetup(serverID, command, storagePtr, chanPtr)
  270.     int serverID;            /* The server for the RPC */
  271.     int command;            /* The RPC to perform */
  272.     register Rpc_Storage *storagePtr;    /* Specifies storage for the RPC
  273.                      * parameters */
  274.     register RpcClientChannel *chanPtr;    /* The channel for the RPC */
  275. {
  276.     register Net_ScatterGather *bufferPtr;    /* This specifies a part of the
  277.                          * packet to the network driver
  278.                          */
  279.     register RpcHdr *rpcHdrPtr;            /* The RPC header */
  280.  
  281.     /*
  282.      * Initialize the RPC header for the request message.  A couple fields
  283.      * are set up elsewhere.  The server hint is left over from previous
  284.      * RPCs.  The channel ID and the version number are set up at
  285.      * boot time by Rpc_Init.
  286.      */
  287.     rpcHdrPtr = (RpcHdr *) &chanPtr->requestRpcHdr;
  288.     if(command == RPC_ECHO_1) {
  289.     rpcHdrPtr->flags = RPC_ECHO;
  290.     } else {
  291.     rpcHdrPtr->flags = RPC_REQUEST;
  292.     }
  293.     rpcHdrPtr->flags |= RPC_SERVER;
  294.  
  295.     rpcHdrPtr->clientID = rpc_SpriteID;
  296.     rpcHdrPtr->serverID = serverID;
  297.     rpcHdrPtr->bootID = rpcBootID;
  298.     rpcHdrPtr->ID = rpcID++;
  299.     rpcHdrPtr->command = command;
  300.  
  301.     /*
  302.      * Setup the timeout parameters depending on the route to the server.
  303.      * This is rather simple minded.
  304.      */
  305.     if (chanPtr->constPtr == (RpcConst *)NIL) {
  306.     Net_Route *routePtr = Net_IDToRoute(serverID, -1, FALSE, 
  307.                 (Sync_Semaphore *) NIL, 0);
  308.     if (routePtr != (Net_Route *)NIL &&
  309.         routePtr->protocol == NET_PROTO_INET) {
  310.         chanPtr->constPtr = &rpcInetConst;
  311.     } else {
  312.         chanPtr->constPtr = &rpcEtherConst;
  313.     }
  314.     if (routePtr != (Net_Route *)NIL) {
  315.         Net_ReleaseRoute(routePtr);
  316.     }
  317.     }
  318.     /*
  319.      * Copy buffer pointers into the state of the channel.
  320.      */
  321.     bufferPtr        = &chanPtr->request.paramBuffer;
  322.     bufferPtr->bufAddr    = storagePtr->requestParamPtr;
  323.     bufferPtr->length    = storagePtr->requestParamSize;
  324.  
  325.     rpcHdrPtr->paramSize = storagePtr->requestParamSize;
  326.  
  327.     bufferPtr        = &chanPtr->request.dataBuffer;
  328.     bufferPtr->bufAddr    = storagePtr->requestDataPtr;
  329.     bufferPtr->length    = storagePtr->requestDataSize;
  330.  
  331.     rpcHdrPtr->dataSize = storagePtr->requestDataSize;
  332.  
  333.     bufferPtr        = &chanPtr->reply.paramBuffer;
  334.     bufferPtr->bufAddr    = storagePtr->replyParamPtr;
  335.     bufferPtr->length    = storagePtr->replyParamSize;
  336.  
  337.     bufferPtr        = &chanPtr->reply.dataBuffer;
  338.     bufferPtr->bufAddr    = storagePtr->replyDataPtr;
  339.     bufferPtr->length    = storagePtr->replyDataSize;
  340.  
  341.     /*
  342.      * Reset state about the reception of replies.
  343.      */
  344.     chanPtr->actualDataSize = 0;
  345.     chanPtr->actualParamSize = 0;
  346.     chanPtr->fragsReceived = 0;
  347.     chanPtr->fragsDelivered = 0;
  348. }
  349. #ifdef DEBUG
  350. #define CHAN_TRACESIZE 1000
  351. #define INC(ctr) { (ctr) = ((ctr) == CHAN_TRACESIZE-1) ? 0 : (ctr)+1; }
  352. typedef struct {
  353.     RpcClientChannel    *chanPtr;
  354.     char        *action;
  355.     int            pNum;
  356.     int            serverID;
  357.     int            chanNum;
  358. } debugElem;
  359.  
  360. static debugElem    debugArray[CHAN_TRACESIZE];
  361. static int         debugCtr;
  362. #define CHAN_TRACE(channel, string) \
  363. {    \
  364.     debugElem *ptr = &debugArray[debugCtr];    \
  365.     INC(debugCtr);    \
  366.     ptr->chanPtr = (channel);    \
  367.     ptr->action = string;    \
  368.     ptr->chanNum = (channel)->index;    \
  369.     ptr->serverID = (channel)->serverID;    \
  370.     ptr->pNum = Mach_GetProcessorNumber();    \
  371. }    
  372. #else
  373. #define CHAN_TRACE(channel, string)
  374. #endif
  375.  
  376.  
  377.  
  378.  
  379. /*
  380.  *----------------------------------------------------------------------
  381.  *
  382.  * GetChannelAllocState --
  383.  *
  384.  *    Get the state of channel allocation in regards to a certain server.
  385.  *
  386.  * Results:
  387.  *    True if the server is marked as being congested.  False otherwise.
  388.  *
  389.  * Side effects:
  390.  *    None.
  391.  *
  392.  *----------------------------------------------------------------------
  393.  */
  394. static Boolean
  395. GetChannelAllocState(serverID, time)
  396.     int        serverID;
  397.     Timer_Ticks    *time;
  398. {
  399.     int        i;
  400.     Boolean    found = FALSE;
  401.  
  402.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  403.     if (serverAllocState[i].serverID == serverID) {
  404.         found = TRUE;
  405.         break;
  406.     }
  407.     }
  408.     if (!found) {
  409.     return FALSE;
  410.     }
  411.     *time = serverAllocState[i].time;
  412.     return TRUE;
  413. }
  414.  
  415.  
  416.  
  417. /*
  418.  *----------------------------------------------------------------------
  419.  *
  420.  * SetChannelAllocState--
  421.  *
  422.  *    Set the state of channel allocation in regards to a certain server.
  423.  *    If we've been getting "noAllocs" back from a server, we want to
  424.  *    ramp down our use of it by using fewer client channels with it.
  425.  *    This routine includes a master lock around it for places where
  426.  *    it's called unprotected.
  427.  *
  428.  * Results:
  429.  *    None.
  430.  *
  431.  * Side effects:
  432.  *    None.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436. static ENTRY void
  437. SetChannelAllocState(serverID, trouble)
  438.     int        serverID;
  439.     Boolean    trouble;
  440. {
  441.  
  442.     MASTER_LOCK(&rpcMutex);
  443.  
  444.     SetChannelAllocStateInt(serverID, trouble);
  445.  
  446.     MASTER_UNLOCK(&rpcMutex);
  447.  
  448.     return;
  449. }
  450.  
  451.  
  452. /*
  453.  *----------------------------------------------------------------------
  454.  *
  455.  * SetChannelAllocStateInt --
  456.  *
  457.  *    Set the state of channel allocation in regards to a certain server.
  458.  *    If we've been getting "noAllocs" back from a server, we want to
  459.  *    ramp down our use of it by using fewer client channels with it.
  460.  *
  461.  * Results:
  462.  *    None.
  463.  *
  464.  * Side effects:
  465.  *    None.
  466.  *
  467.  *----------------------------------------------------------------------
  468.  */
  469. static void
  470. SetChannelAllocStateInt(serverID, trouble)
  471.     int        serverID;
  472.     Boolean    trouble;
  473. {
  474.     int        i;
  475.     Boolean    found = FALSE;
  476.  
  477.  
  478.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  479.     if (serverAllocState[i].serverID == serverID) {
  480.         /* already marked as unhappy */
  481.         found = TRUE;
  482.         break;
  483.     }
  484.     }
  485.     if (!found && !trouble) {
  486.     /* server isn't already marked as being in trouble. */
  487.     return;
  488.     }
  489.     if (!found) {
  490.     /* Server is in trouble, we need to record this. */
  491.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer));
  492.         i++) {
  493.         if (serverAllocState[i].serverID == -1) {
  494.         found = TRUE;
  495.         break;
  496.         }
  497.     }
  498.     if (!found) {
  499.         /* No more spaces to mark unhappy server! */
  500.         rpcCltStat.noMark++;
  501.         printf("SetChannelAllocStateInt: %s\n",
  502.             "No more room to keep track of congested servers.");
  503.         return;
  504.     }
  505.     }
  506.     if (trouble) {
  507.     Timer_GetCurrentTicks(&(serverAllocState[i].time));
  508.     if (serverAllocState[i].serverID == -1) {
  509.         rpcCltStat.newTrouble++;
  510.     } else {
  511.         rpcCltStat.moreTrouble++;
  512.     }
  513.     serverAllocState[i].serverID = serverID;
  514.     } else {
  515.     rpcCltStat.endTrouble++;
  516.     serverAllocState[i].serverID = -1;
  517.     }
  518.  
  519.     return;
  520. }
  521.  
  522.  
  523. /*
  524.  *----------------------------------------------------------------------
  525.  *
  526.  * RpcChanAlloc --
  527.  *
  528.  *      Allocate a channel for an RPC.  A pointer to the channel is
  529.  *      returned.  The allocation is done on the basis of the server
  530.  *      machine involved.  The goal is to send a long series of RPC
  531.  *      requests to the same server over the same channel.  To that end a
  532.  *      channel is chosen first if its cached address matches the input
  533.  *      server address.  Second choice is a previously unused channel,
  534.  *      lastly we re-use a channel that has been used with a different server.
  535.  *
  536.  * Results:
  537.  *    A pointer to the channel.  This returns a good pointer, or it panics.
  538.  *
  539.  * Side effects:
  540.  *    The channel is dedicated to the RPC until the caller frees
  541.  *    the channel with RpcChanFree.
  542.  *
  543.  *----------------------------------------------------------------------
  544.  */
  545. ENTRY RpcClientChannel *
  546. RpcChanAlloc(serverID)
  547.     int serverID;    /* Server ID to base our allocation on. */
  548. {
  549.     RpcClientChannel *chanPtr = (RpcClientChannel *) NULL;
  550.                     /* The channel we allocate */
  551.     register int i;            /* Index into channel table */
  552.     int firstUnused = -1;        /* The first unused channel */
  553.     int firstBusy = -1;            /* The first busy channel for server */
  554.     int firstFreeMatch = -1;        /* The first chan free for server */
  555.     int firstFree = -1;            /* The first chan used but now free */
  556.     Timer_Ticks    time;            /* When server channel state set. */
  557.     Timer_Ticks    currentTime;        /* Current ticks. */
  558.     Boolean    srvCongested;        /* Server is marked as congested. */
  559.     unsigned int smallestID = UINT_MAX;
  560.  
  561.     MASTER_LOCK(&rpcMutex);
  562.  
  563.     while (numFreeChannels < 1) {
  564.     rpcCltStat.chanWaits++;
  565. waitForBusyChannel:
  566.     Sync_MasterWait(&freeChannels, &rpcMutex, FALSE);
  567.     }
  568.     firstUnused = -1;
  569.     firstBusy = -1;
  570.     firstFreeMatch = -1;
  571.     firstFree = -1;
  572.  
  573.     srvCongested = GetChannelAllocState(serverID, &time);
  574.     if (srvCongested) {
  575.     Timer_AddIntervalToTicks(time, channelStateInterval, &time);
  576.     Timer_GetCurrentTicks(¤tTime);
  577.     }
  578.     if (srvCongested && (Timer_TickGE(time, currentTime))) {
  579.     /*
  580.      * Server is congested, so ramp down use of channels.
  581.      * If there's a channel, free or busy, for our server, use the
  582.      * lowest numbered one.  If it's busy, wait till it's not.  If there's
  583.      * no channel for this server, take free one.
  584.      */
  585.     for (i=0 ; i<rpcNumChannels ; i++) {
  586.         chanPtr = rpcChannelPtrPtr[i];
  587.         if (serverID == chanPtr->serverID) {
  588.         if (chanPtr->state == CHAN_FREE) {
  589.             if (firstFreeMatch < 0) {
  590.             firstFreeMatch = i;
  591.             }
  592.         } else {
  593.             if (firstBusy < 0) {
  594.             firstBusy = i;
  595.             }
  596.         }
  597.         } else if (chanPtr->serverID == -1) {
  598.         if (firstUnused < 0) {
  599.             firstUnused = i;
  600.         }
  601.         } else {
  602.         if (chanPtr->state == CHAN_FREE) {
  603.             if (firstFree < 0)  {
  604.             firstFree = i;
  605.             }
  606.         }
  607.         }
  608.     }
  609.     if (firstFreeMatch >= 0 &&
  610.         (firstBusy == -1 || firstBusy > firstFreeMatch)) {
  611.         /*
  612.          * We've found a free channel matching our server ID and there
  613.          * isn't a busy one for our server ID of lower number, so take
  614.          * this one.
  615.          */
  616.         chanPtr = rpcChannelPtrPtr[firstFreeMatch];
  617.         goto found;
  618.     }
  619.     if (firstBusy > 0) {
  620.         /*
  621.          * There's a busy channel matching our server ID and either it's
  622.          * of lower number than a free channel matching our serverID or
  623.          * else there's no free channel matching our server ID.  Wait for
  624.          * this one to free up.
  625.          */
  626.         rpcCltStat.nackChanWait++;
  627.         goto waitForBusyChannel;
  628.     }
  629.     /*
  630.      * Otherwise, there's no free our busy channel matching our server ID
  631.      * so we'll create one in the code later down below.
  632.      */
  633.     } else {
  634.     /*
  635.      * Server is not congested.  Make sure it's marked okay, and allocate
  636.      * a channel in the regular fasion.
  637.      */
  638.     if (srvCongested) {
  639.         /* Mark server as okay now. */
  640.         SetChannelAllocStateInt(serverID, FALSE);
  641.     }
  642.     /* use regular alloc */
  643.  
  644.     for (i=0 ; i<rpcNumChannels ; i++) {
  645.         chanPtr = rpcChannelPtrPtr[i];
  646.         if (chanPtr->state == CHAN_FREE) {
  647.         if (chanPtr->serverID == -1) {
  648.             /*
  649.              * Remember the first unused channel.
  650.              */
  651.             if (firstUnused < 0) {
  652.             firstUnused = i;
  653.             }
  654.         } else if (serverID == chanPtr->serverID) {
  655.             /*
  656.              * Agreement between the channels old server and the
  657.              * server ID.  By reusing this channel we hope to give
  658.              * the server an implicit acknowledgment for the
  659.              * previous transaction.
  660.              */
  661.             rpcCltStat.chanHits++;
  662.             CHAN_TRACE(chanPtr, "alloc channel w/ same server");
  663.             goto found;
  664.         } else if (chanPtr->requestRpcHdr.ID < smallestID) {
  665.             /*
  666.              * Keep track of the channel with the smallest rpcID
  667.              * (it's the least recently used). We'll use this one
  668.              * if we can't reuse a channel with the same server or
  669.              * find an unused channel.
  670.              */
  671.             firstFree = i;
  672.             smallestID = chanPtr->requestRpcHdr.ID;
  673.         }
  674.         }
  675.     }
  676.     }
  677.     /*
  678.      * We didn't find an address match on a free channel, so we use
  679.      * the first previously unused channel or the first used but free channel.
  680.      */
  681.     if (firstUnused >= 0) {
  682.     rpcCltStat.chanNew++;
  683.     chanPtr = rpcChannelPtrPtr[firstUnused];
  684.     CHAN_TRACE(chanPtr, "alloc first unused");
  685.     } else if (firstFree >= 0) {
  686.     rpcCltStat.chanReuse++;
  687.     chanPtr = rpcChannelPtrPtr[firstFree];
  688.     CHAN_TRACE(chanPtr, "alloc first free");
  689.     } else {
  690.     panic("Rpc_ChanAlloc can't find the free channel.\n");
  691.     }
  692.     chanPtr->serverID = serverID;
  693.     chanPtr->constPtr = (RpcConst *)NIL;    /* Set in RpcSetup */
  694.  
  695. found:
  696.     chanPtr->state = CHAN_BUSY;
  697.     numFreeChannels--;
  698.  
  699.     MASTER_UNLOCK(&rpcMutex);
  700.     return(chanPtr);
  701. }
  702.  
  703. /*
  704.  *----------------------------------------------------------------------
  705.  *
  706.  * RpcChanFree --
  707.  *
  708.  *    Free an RPC channel.
  709.  *
  710.  * Results:
  711.  *    None.
  712.  *
  713.  * Side effects:
  714.  *    The channel is available for other RPC calls.
  715.  *
  716.  *----------------------------------------------------------------------
  717.  */
  718. ENTRY void
  719. RpcChanFree(chanPtr)
  720.     RpcClientChannel *chanPtr;        /* The channel to free */
  721. {
  722.  
  723.     MASTER_LOCK(&rpcMutex);
  724.     CHAN_TRACE(chanPtr, "free channel");
  725.     if (chanPtr->state == CHAN_FREE) {
  726.     panic("Rpc_ChanFree: freeing free channel\n");
  727.     }
  728.     chanPtr->state = CHAN_FREE;
  729.  
  730.     numFreeChannels++;
  731.     if (numFreeChannels == 1 || rpcChannelNegAcks) {
  732.     rpcCltStat.chanBroads++;
  733.     Sync_MasterBroadcast(&freeChannels);
  734.     }
  735.     
  736.     MASTER_UNLOCK(&rpcMutex);
  737. }
  738.  
  739. /*
  740.  *----------------------------------------------------------------------
  741.  *
  742.  * RpcChanClose --
  743.  *
  744.  *    Process a close request from a server. This is called from
  745.  *    RpcClientDispatch at interrupt level.  If the channel is not
  746.  *    busy then the interrupt handler
  747.  *    needs to mark the channel as busy momentarily and send an
  748.  *    ack to the server. See RpcClientDispatch for more details.
  749.  *
  750.  * Results:
  751.  *    None.
  752.  *
  753.  * Side effects:
  754.  *    The channel might be used to send an ack to the server.
  755.  *
  756.  *----------------------------------------------------------------------
  757.  */
  758.  
  759. void
  760. RpcChanClose(chanPtr,rpcHdrPtr)
  761.     register RpcClientChannel *chanPtr;    /* The channel to use.*/
  762.     register RpcHdr *rpcHdrPtr;        /* The Rpc header as it sits in the
  763.                      * network module's buffer.  The data
  764.                      * in the message follows this. */
  765. {
  766.     register RpcHdr *ackHdrPtr;
  767.     if ((chanPtr->state & CHAN_BUSY) == 0) {
  768.     MASTER_LOCK(&rpcMutex);
  769.     /*
  770.      * Check again to make sure the channel isn't busy,
  771.      * then temporarily allocate it while we issue the explicit ack.
  772.      * If a process has slipped in and allocated the channel then its
  773.      * request will serve as the acknowledgement and we can bail out.
  774.      */
  775.     if ((chanPtr->state & CHAN_BUSY) != 0) {
  776.         MASTER_UNLOCK(&rpcMutex);
  777.         return;
  778.     }
  779.     chanPtr->state |= CHAN_BUSY;
  780.     numFreeChannels--;
  781.     MASTER_UNLOCK(&rpcMutex);
  782.  
  783.     rpcCltStat.close++;
  784.     /*
  785.      * Set up and transmit the explicit acknowledgement packet.
  786.      * Note that fields that never change have already been
  787.      * set in RpcBufferInit.
  788.      */
  789.     ackHdrPtr = &chanPtr->ackHdr;
  790.     ackHdrPtr->flags = RPC_ACK | RPC_CLOSE | RPC_SERVER;
  791.     ackHdrPtr->clientID = rpc_SpriteID;
  792.     ackHdrPtr->serverID = rpcHdrPtr->serverID;
  793.     ackHdrPtr->serverHint = rpcHdrPtr->serverHint;
  794.     ackHdrPtr->command = rpcHdrPtr->command;
  795.     ackHdrPtr->bootID = rpcBootID;
  796.     ackHdrPtr->ID = rpcHdrPtr->ID;
  797.     (void)RpcOutput(rpcHdrPtr->serverID, &chanPtr->ackHdr, &chanPtr->ack,
  798.                  (RpcBufferSet *)NIL, 0, (Sync_Semaphore *)NIL);
  799.     /*
  800.      * Note that the packet can linger in the network output queue,
  801.      * so we are relying on the fact that it would only be reused
  802.      * for another ack in the worst case.
  803.      */
  804.  
  805.     MASTER_LOCK(&rpcMutex);
  806.     chanPtr->state &= ~CHAN_BUSY;
  807.     numFreeChannels++;
  808.     MASTER_UNLOCK(&rpcMutex);
  809.     }
  810. }
  811.  
  812.  
  813. /*
  814.  *----------------------------------------------------------------------
  815.  *
  816.  * RpcInitServerChannelState --
  817.  *
  818.  *    Initialize data about the client's view of how the servers are doing.
  819.  *
  820.  * Results:
  821.  *    None.
  822.  *
  823.  * Side effects:
  824.  *    None.
  825.  *
  826.  *----------------------------------------------------------------------
  827.  */
  828. void
  829. RpcInitServerChannelState()
  830. {
  831.     int        i;
  832.  
  833.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  834.     serverAllocState[i].serverID = -1;
  835.     serverAllocState[i].time = timer_TicksZeroSeconds;
  836.     }
  837.     channelStateInterval = timer_IntOneSecond * 10;
  838. }
  839.